﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Monopoly.Domain
{
    public class Gameboard
    {
        private const short MAX_BOXES = 40;
        private const short MAX_CHANCES = 16;
        private const short MAX_COMMUNITYCHESTS = 16;
        private const short MAX_PIECES = 6;
        private Dictionary<ConsoleColor, int> FieldCountByColor;

        private IList<Box> Boxes;
        private IList<Chance> ChanceDeck;
        private IList<CommunityChest> CommunityChestDeck;
        private IList<Piece> Pieces;

        public Gameboard()
        {
            this.CreateBoxes();
            this.CreateCardStacks();
            this.CreatePieces();
        }

        /// <summary>
        /// Initialize the list of Gameboard boxes
        /// </summary>
        private void CreateBoxes()
        {
            this.FieldCountByColor = new Dictionary<ConsoleColor, int>();
            this.FieldCountByColor.Add(ConsoleColor.Magenta, 2);
            this.FieldCountByColor.Add(ConsoleColor.Cyan, 3);
            this.FieldCountByColor.Add(ConsoleColor.Red, 3);
            this.FieldCountByColor.Add(ConsoleColor.DarkMagenta, 3);
            this.FieldCountByColor.Add(ConsoleColor.DarkRed, 3);
            this.FieldCountByColor.Add(ConsoleColor.Yellow, 3);
            this.FieldCountByColor.Add(ConsoleColor.Green, 3);
            this.FieldCountByColor.Add(ConsoleColor.DarkBlue, 2);

            this.Boxes = new List<Box>();
            this.Boxes.Add(new BoxAction("Départ", BoxActionType.PassOverGo));
            this.Boxes.Add(new Field("Boul. de Belleville", ConsoleColor.Magenta, 6000));
            this.Boxes.Add(new BoxAction("Caisse de communauté", BoxActionType.PickACommunityChestCard));
            this.Boxes.Add(new Field("Rue Lecourbe", ConsoleColor.Magenta, 6000));
            this.Boxes.Add(new BoxAction("Impôts sur le revenu", BoxActionType.PayATax));
            this.Boxes.Add(new Station("Gare Montparnasse"));
            this.Boxes.Add(new Field("Rue de vaugirard", ConsoleColor.Cyan, 10000));
            this.Boxes.Add(new BoxAction("Chance", BoxActionType.PickAChanceCard));
            this.Boxes.Add(new Field("Rue de courcelles", ConsoleColor.Cyan, 10000));
            this.Boxes.Add(new Field("Avenue de la République", ConsoleColor.Cyan, 12000));
            this.Boxes.Add(new BoxAction("Prison", BoxActionType.VisitJail));
            this.Boxes.Add(new Field("Boul. de la Vilette", ConsoleColor.Red, 14000));
            this.Boxes.Add(new Company("Compagnie de distr. d'électricité"));
            this.Boxes.Add(new Field("Avenue de Neuilly", ConsoleColor.Red, 14000));
            this.Boxes.Add(new Field("Rue de paradis", ConsoleColor.Red, 16000));
            this.Boxes.Add(new Station("Gare de Lyon"));
            this.Boxes.Add(new Field("Avenue Mozart", ConsoleColor.DarkMagenta, 18000));
            this.Boxes.Add(new BoxAction("Caisse de communauté", BoxActionType.PickACommunityChestCard));
            this.Boxes.Add(new Field("Boulevard Saint-Michel", ConsoleColor.DarkMagenta, 18000));
            this.Boxes.Add(new Field("Place Pigalle", ConsoleColor.DarkMagenta, 20000));
            this.Boxes.Add(new BoxAction("Parc gratuit", BoxActionType.FreeParking));
            this.Boxes.Add(new Field("Avenue Matignon", ConsoleColor.DarkRed, 22000));
            this.Boxes.Add(new BoxAction("Chance", BoxActionType.PickAChanceCard));
            this.Boxes.Add(new Field("Boulevard Malesherbes", ConsoleColor.DarkRed, 22000));
            this.Boxes.Add(new Field("Avenue Henri-Martin", ConsoleColor.DarkRed, 24000));
            this.Boxes.Add(new Station("Gare du nord"));
            this.Boxes.Add(new Field("Faubourg Saint-Honoré", ConsoleColor.Yellow, 26000));
            this.Boxes.Add(new Field("Place de la bourse", ConsoleColor.Yellow, 26000));
            this.Boxes.Add(new Company("Compagnie de distr. des eaux"));
            this.Boxes.Add(new Field("Rue de la Fayette", ConsoleColor.Yellow, 28000));
            this.Boxes.Add(new BoxAction("Aller en prison", BoxActionType.GoToJail));
            this.Boxes.Add(new Field("Avenue de Breteuil", ConsoleColor.Green, 30000));
            this.Boxes.Add(new Field("Avenue Foch", ConsoleColor.Green, 30000));
            this.Boxes.Add(new BoxAction("Caisse de communauté", BoxActionType.PickACommunityChestCard));
            this.Boxes.Add(new Field("Boul. des Capucines", ConsoleColor.Green, 32000));
            this.Boxes.Add(new Station("Gare Saint-Lazare"));
            this.Boxes.Add(new BoxAction("Chance", BoxActionType.PickAChanceCard));
            this.Boxes.Add(new Field("Avenue des Champs-Élysées", ConsoleColor.DarkBlue, 35000));
            this.Boxes.Add(new BoxAction("Taxe de luxe", BoxActionType.PayATax));
            this.Boxes.Add(new Field("Rue de la paix", ConsoleColor.DarkBlue, 40000));
        }

        /// <summary>
        /// Initialize community chest and chance card stacks
        /// </summary>
        private void CreateCardStacks()
        {
            this.ChanceDeck = new List<Chance>();
            this.ChanceDeck.Add(new Chance("Allez à la case départ", CardActionType.MoveOnBox, 1));
            this.ChanceDeck.Add(new Chance("Allez en prison", CardActionType.GoToJail, 11));
            this.ChanceDeck.Add(new Chance("Allez Boul. de la Vilette", CardActionType.MoveOnBox, 12));
            this.ChanceDeck.Add(new Chance("Amende pour ivresse", CardActionType.PayFreeParking, 2000));
            this.ChanceDeck.Add(new Chance("Vous avez gagné aux mots-croisés", CardActionType.ReceiveBankMoney, 10000));
            this.ChanceDeck.Add(new Chance("Allez rue Henri-Martin", CardActionType.MoveOnBox, 25));
            this.ChanceDeck.Add(new Chance("Allez à la Gare de Lyon", CardActionType.MoveOnBox, 16));
            this.ChanceDeck.Add(new Chance("Amende pour excès de vitesse", CardActionType.PayFreeParking, 1500));
            this.ChanceDeck.Add(new Chance("Payez vos frais de scolarité", CardActionType.PayBank, 15000));
            this.ChanceDeck.Add(new Chance("Reculez de trois cases", CardActionType.Move, -3));
            //this.ChanceDeck.Add(new Chance("Réparez vos maisons et hôtels", CardActionType.PayBank));
            this.ChanceDeck.Add(new Chance("Votre prêt vous rapporte de l'argent", CardActionType.ReceiveBankMoney, 15000));
            this.ChanceDeck.Add(new Chance("Vous êtes libérés de prison", CardActionType.WalkFreeOutOfJail));
            this.ChanceDeck.Add(new Chance("Rendez vous Rue de la Paix", CardActionType.MoveOnBox, 40));
            //this.ChanceDeck.Add(new Chance("Travaux de voirie", CardActionType.PayBank));
            this.ChanceDeck.Add(new Chance("La banque vous doit de l'argent", CardActionType.ReceiveBankMoney, 5000));

            this.CommunityChestDeck = new List<CommunityChest>();
            this.CommunityChestDeck.Add(new CommunityChest("Payez votre police d'assurance", CardActionType.PayBank, 5000));
            this.CommunityChestDeck.Add(new CommunityChest("Vous héritez de " + (10000).ToEuro(), CardActionType.ReceiveBankMoney, 10000));
            this.CommunityChestDeck.Add(new CommunityChest("Vous êtes libérés de prison", CardActionType.WalkFreeOutOfJail));
            this.CommunityChestDeck.Add(new CommunityChest("Note de frais du médecin", CardActionType.PayBank, 5000));
            this.CommunityChestDeck.Add(new CommunityChest("Vous gagnez le second prix de beauté", CardActionType.ReceiveBankMoney, 1000));
            this.CommunityChestDeck.Add(new CommunityChest("Vos contributions vous rembourse", CardActionType.ReceiveBankMoney, 2000));
            this.CommunityChestDeck.Add(new CommunityChest("Recevez vos intérêts", CardActionType.ReceiveBankMoney, 2500));
            this.CommunityChestDeck.Add(new CommunityChest("Retournez à Belleville", CardActionType.MoveOnBox, 2));
            this.CommunityChestDeck.Add(new CommunityChest("Allez en prison", CardActionType.GoToJail, 11));
            this.CommunityChestDeck.Add(new CommunityChest("Placez-vous sur la case départ", CardActionType.MoveOnBox, 1));
            this.CommunityChestDeck.Add(new CommunityChest("C'est votre anniversaire !", CardActionType.ReceivePlayerMoney, 2000));
            this.CommunityChestDeck.Add(new CommunityChest("Recevez " + (10000).ToEuro(), CardActionType.ReceiveBankMoney, 10000));
            this.CommunityChestDeck.Add(new CommunityChest("Erreur de la banque en votre faveur", CardActionType.ReceiveBankMoney, 2000));
            this.CommunityChestDeck.Add(new CommunityChest("Ventre de votre stock", CardActionType.ReceiveBankMoney, 5000));
            this.CommunityChestDeck.Add(new CommunityChest("Payer l'hôpital", CardActionType.PayBank, 10000));
        }

        /// <summary>
        /// Create the six first pieces of the gameboard
        /// </summary>
        private void CreatePieces()
        {
            this.Pieces = new List<Piece>();
            this.Pieces.Add(new Piece("Chevalier"));
            this.Pieces.Add(new Piece("Voiture"));
            this.Pieces.Add(new Piece("Bateau"));
            this.Pieces.Add(new Piece("Chaussure"));
            this.Pieces.Add(new Piece("Chapeau"));
            this.Pieces.Add(new Piece("Dé à coudre"));
        }

        /// <summary>
        /// Delete the first available piece and returns it
        /// </summary>
        /// <returns>A Piece ofthe game or null if there isn't any piece available yet</returns>
        public Piece PopAvailablePiece()
        {
            if (this.Pieces.Count > 0)
            {
                Piece Piece = this.Pieces.First();
                this.Pieces.RemoveAt(0);
                return Piece;
            }
            else return null;
        }

        public void PushCommunityChestCard(CommunityChest CommunityChestCard)
        {
            this.CommunityChestDeck.Add(CommunityChestCard);
        }

        public void PushChanceCard(Chance ChanceCard)
        {
            this.ChanceDeck.Add(ChanceCard);
        }

        /// <summary>
        /// Compute and returns a list of salable boxes
        /// </summary>
        /// <returns>A list of salable boxes</returns>
        /// <remarks>This method is used in bank initialization</remarks>
        public IList<Box> GetPropertyList()
        {
            return this.Boxes.Where(b => b is BoxProperty).ToList();
        }

        public void AddCards(IList<Card> Cards)
        {
            foreach (Card Card in Cards)
            {
                if (Card is Chance)
                    this.ChanceDeck.Add((Chance)Card);
                else
                    this.CommunityChestDeck.Add((CommunityChest)Card);
            }
        }

        public Chance PopChanceCard()
        {
            Chance ChanceCard = this.ChanceDeck[0];
            this.ChanceDeck.RemoveAt(0);
            return ChanceCard;
        }

        public CommunityChest PopCommunityChestCard()
        {
            CommunityChest CommunityChestCard = this.CommunityChestDeck[0];
            this.CommunityChestDeck.RemoveAt(0);
            return CommunityChestCard;
        }

        /// <summary>
        /// Return a specific box
        /// </summary>
        /// <param name="BoxIndex">The box index</param>
        /// <returns>A Box</returns>
        public Box GetBox(int BoxIndex)
        {
            if (BoxIndex <= 40 && BoxIndex >= 0)
                return this.Boxes[BoxIndex - 1];
            else
                throw new InvalidOperationException();
        }

        public int CountFieldsByColor(ConsoleColor ConsoleColor)
        {
            return this.FieldCountByColor[ConsoleColor];
        }
    }
}
